#pragma once

#include <iostream>
#include <string>
#include <vector>

/*
    用解释器模式设计一个"厦漳泉"公交车卡的读卡器程序。

    说明：假如"厦漳泉"公交车读卡器可以判断乘客的身份，如果是这三座城市的"老人"" "妇女" "儿童"就可以免费乘车，其他人员乘车一次扣 2 元。
*/
/*
    文法规则
    <expression> ::= <city>的<person>
    <city> ::= 厦门|泉州|漳州
    <person> ::= 老人|妇女|儿童
*/

namespace interpreter {

/// @brief 抽象表达式, 定义解释器的接口，约定解释器的解释操作，主要包含解释方法 interpret()。
class AbstractExpression {
public:
    virtual ~AbstractExpression() {}

    /// @brief 解释方法
    virtual bool Interpret(const std::string& info) = 0;
};

/// @brief 终结符表达式, 是抽象表达式的子类，用来实现文法中与终结符相关的操作，文法中的每一个终结符都有一个具体终结表达式与之相对应。
class TerminalExpression : public AbstractExpression {
public:
    TerminalExpression(const std::vector<std::string>& content) : contents_(content) {}

    virtual bool Interpret(const std::string& info) {
        for (auto& content : contents_) {
            if (info == content) return true;
            std::size_t len = info.size();
            for (int i = 0; i < content.size(); i++) {
                if (i + len  <= content.size() && content.substr(i, i+len).compare(info) == 0) {
                    return true;
                }
            }
        }
        return false;
    }

private:
    std::vector<std::string> contents_;
};

/// @brief 非终结符表达式, 也是抽象表达式的子类，用来实现文法中与非终结符相关的操作，文法中的每条规则都对应于一个非终结符表达式。
class NonterminalExpression : public AbstractExpression {
public:
    NonterminalExpression(std::shared_ptr<AbstractExpression>& city_eps, std::shared_ptr<AbstractExpression>& person_eps) : city_eps_(city_eps), person_eps_(person_eps) {}

    virtual bool Interpret(const std::string& info) {
        std::vector<std::string> split_vec;
        if (2 != Split(info, '|', split_vec)) {
            return false;
        }
        return city_eps_->Interpret(split_vec[0]) && person_eps_->Interpret(split_vec[1]);
    }

protected:
    size_t Split(const std::string& src, const char ch, std::vector<std::string>& vec) {
        size_t nPos = 0;
        std::string tmp;
        for (size_t i = 0; i < src.size(); i++) {
            if (src[i] == ch) {
                tmp = src.substr(nPos, i - nPos);
                if (!tmp.empty() && tmp != "\n")
                    vec.push_back(tmp);
                nPos = i + 1;
            }
        }
        if (nPos <= src.size()) {
            tmp = src.substr(nPos, src.size() - nPos);
            if (!tmp.empty() && tmp != "\n")
                vec.push_back(tmp);
        }
        return vec.size();
    }

private:
    std::shared_ptr<AbstractExpression> city_eps_ = nullptr;
    std::shared_ptr<AbstractExpression> person_eps_ = nullptr;
};

/// @brief 环境类, 通常包含各个解释器需要的数据或是公共的功能，一般用来传递被所有解释器共享的数据，后面的解释器可以从这里获取这些值。
class Context {
public:
    Context() {
        std::vector<std::string> citys;
        std::vector<std::string> persons;

        citys.emplace_back("厦门");
        citys.emplace_back("漳州");
        citys.emplace_back("泉州");

        persons.emplace_back("老人");
        persons.emplace_back("妇女");
        persons.emplace_back("儿童");

        std::shared_ptr<AbstractExpression> city_eps(new TerminalExpression(citys));
        std::shared_ptr<AbstractExpression> person_eps(new TerminalExpression(persons));
        
        eps_.reset(new NonterminalExpression(city_eps, person_eps));
    }
    void FreeRide(const std::string& info) {
        if (eps_ && eps_->Interpret(info)) {
            printf("身份识别: %s, 您本次乘车免费！\n", info.c_str());
        }
        else {
            printf("身份识别: %s, 本次乘车扣费2元！\n", info.c_str());
        }
    }

protected:
    std::shared_ptr<AbstractExpression> eps_;
};

} // namespace interpreter

void Test_23_visitor_impl_1() {
    printf("-------------------- %s --------------------\n", __FUNCTION__);
    std::shared_ptr<interpreter::Context> context(new interpreter::Context());
    context->FreeRide("厦门|老人");
    context->FreeRide("漳州|妇女");
    context->FreeRide("泉州|儿童");
    context->FreeRide("上海|老人");
    context->FreeRide("厦门|青年");
}

// -------------------- Test_23_visitor_impl_1 --------------------
// 身份识别: 厦门|老人, 您本次乘车免费！
// 身份识别: 漳州|妇女, 您本次乘车免费！
// 身份识别: 泉州|儿童, 您本次乘车免费！
// 身份识别: 上海|老人, 本次乘车扣费2元！
// 身份识别: 厦门|青年, 本次乘车扣费2元！